<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>vue3入门笔记 | dmf的个人博客</title><meta name="author" content="dmf"><meta name="copyright" content="dmf"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="描述">
<meta property="og:type" content="article">
<meta property="og:title" content="vue3入门笔记">
<meta property="og:url" content="https://gitee.com/dmf-c/blog/2023/01/08/vue_file/vuenote/index.html">
<meta property="og:site_name" content="dmf的个人博客">
<meta property="og:description" content="描述">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://img1.baidu.com/it/u=1828439677,2166285235&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=300">
<meta property="article:published_time" content="2023-01-07T16:00:00.000Z">
<meta property="article:modified_time" content="2023-02-06T01:28:47.377Z">
<meta property="article:author" content="dmf">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img1.baidu.com/it/u=1828439677,2166285235&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=300"><link rel="shortcut icon" href="/blog/img/favicon.png"><link rel="canonical" href="https://gitee.com/dmf-c/blog/2023/01/08/vue_file/vuenote/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/blog/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/blog/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'vue3入门笔记',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-02-06 09:28:47'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/blog/archives/"><div class="headline">文章</div><div class="length-num">27</div></a><a href="/blog/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/blog/categories/"><div class="headline">分类</div><div class="length-num">6</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/blog/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/blog/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/blog/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/blog/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/blog/">dmf的个人博客</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/blog/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/blog/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/blog/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/blog/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">vue3入门笔记</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="fa-fw post-meta-icon far fa-calendar-alt"></i><span class="post-meta-label">发表于</span><time datetime="2023-01-07T16:00:00.000Z" title="发表于 2023-01-08 00:00:00">2023-01-08</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/blog/categories/Vue/">Vue</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="vue3入门笔记"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="Vue3-简介"><a href="#Vue3-简介" class="headerlink" title="Vue3 简介"></a>Vue3 简介</h2><p>面临的问题：随着功能的增长，复杂组件的代码变得难以维护，<code>Vue3</code> 就随之而来，<code>TypeScript</code> 使用的越来越多，<code>Vue3</code>就是 <code>TS</code> 写的所以能够更好的支持 <code>TypeScript</code></p>
<p><code>vue2</code> 的绝大多数的特性 在 <code>Vue3</code> 都能使用，毕竟 Vue 是渐进式的</p>
<p>响应式原理进行使用 <code>Proxy</code> 实现，<code>v-model</code> 可以传参了等等新特性</p>
<h2 id="基础工作"><a href="#基础工作" class="headerlink" title="基础工作"></a>基础工作</h2><p>使用<code>Vue3</code>的话，那么必须通过使用构建工具创建一个 <code>Vue3</code> 项目</p>
<h3 id="安装-vue-cli"><a href="#安装-vue-cli" class="headerlink" title="安装 vue-cli"></a>安装 vue-cli</h3><pre><code># npm
npm install -g @vue/cli
# yarn
yarn global add @vue/cli
</code></pre>
<h3 id="创建一个项目"><a href="#创建一个项目" class="headerlink" title="创建一个项目"></a>创建一个项目</h3><p>使用 <code>create</code> 命令行创建 或者 用 <code>ui</code> 可视化创建</p>
<pre><code># create
vue create 项目名
# 可视化
vue ui
</code></pre>
<p>当然也可以选择 <code>vite</code> <code>，vite</code> 创建的速度比 上面的方法快了一些</p>
<pre><code>npm init vite-app 项目名
cd 项目名
npm install
npm run dev
</code></pre>
<h2 id="Vue3-入门"><a href="#Vue3-入门" class="headerlink" title="Vue3 入门"></a>Vue3 入门</h2><h3 id="Composition-API"><a href="#Composition-API" class="headerlink" title="Composition API"></a>Composition API</h3><p><strong>Vue3 提出了 Composition API</strong></p>
<p>在 <code>Vue2.X</code> 我们使用的是 <code>OptionAPI</code> 里面有我们熟悉的 <code>data、computed、methods、watch...</code></p>
<p>在 <code>Vue3</code> 中，我们依旧可以使用 <code>OptionAPI</code>当然不建议 和 <code>Vue3</code> 混用<br>在 <code>Vue2</code> 中，我们实现一个功能得分到不同的地方，把数据放在 <code>data </code>,<code>computed</code> 方法放在 <code>methods</code> 里面，分开的太散乱了，几个功能还好，几十个上百个，那就有点…</p>
<p>所以 <code>Vue3</code> 提出了 <code>Composition API</code> ，它可以把 <strong>一个逻辑的代码都收集在一起</strong> 单独写个<code>hook</code>,然后再引入，这样就不到处分布，显得很乱了</p>
<h3 id="Fragment"><a href="#Fragment" class="headerlink" title="Fragment"></a>Fragment</h3><p><strong>在 template 中不再需要一个根元素包裹</strong></p>
<pre><code>&lt;template&gt;
&lt;img alt=&quot;Vue logo&quot; src=&quot;./assets/logo.png&quot; /&gt;
&lt;HelloWorld msg=&quot;Welcome to Your Vue.js + TypeScript App&quot; /&gt;
&lt;/template&gt;
</code></pre>
<p>实际上内部会将多个标签包含在一个<code>Fragment</code>虚拟元素中</p>
<p>好处: 减少标签层级, 减小内存占用</p>
<h3 id="script-差异"><a href="#script-差异" class="headerlink" title="script 差异"></a>script 差异</h3><p>来看看 <code>script</code> 和 <code>Vue2</code> 的区别</p>
<pre><code>&lt;script lang=&quot;ts&quot;&gt;
import &#123; defineComponent&#125; from &#39;vue&#39;

export default defineComponent(&#123;
name: &#39;App&#39;,
setup() &#123;
    return &#123;
        // 这里的属性 和 方法 会合并到 data 函数 和 methods 对象里
    &#125;
&#125;,
&#125;)
&lt;/script&gt;
</code></pre>
<ul>
<li>可以再 <code>script</code> 使用 <code>ts</code> 只需 设置 <code>lang</code> 即可</li>
<li><code>defineComponent</code> 方法创建一个组件</li>
<li><code>export default</code> 直接导出一个组件</li>
</ul>
<h3 id="setup"><a href="#setup" class="headerlink" title="setup"></a>setup</h3><p><code>setup</code> 是 <code>Composition API</code>的入口</p>
<h4 id="setup-执行顺序"><a href="#setup-执行顺序" class="headerlink" title="setup 执行顺序"></a><code>setup</code> 执行顺序</h4><p>它在<code>beforeCreate</code>之前执行一次，<code>beforeCreate</code>这个钩子 的任务就是初始化，在它之前执行，那么 <code>this</code> 就没有被初始化 <code>this = undefined</code> 这样就不能通过 <code>this</code> 来调用方法 和 获取属性</p>
<p><img src="http://test.imgurl.org/imgs/2023/01/31/bbaa65eec3a4312c.png"></p>
<h4 id="setup-返回值"><a href="#setup-返回值" class="headerlink" title="setup 返回值"></a>setup 返回值</h4><p><code>setup</code> 返回的是一个<code>对象</code>，这个对象的属性会与组件中 <code>data</code> 函数返回的对象进行<code>合并</code>，返回的方法和 <code>methods</code> 合并，合并之后直接可以在模板中使用，如果有重名的情况，会使用 <code>setup</code> 返回的<code>属性</code>和<code>方法``，methods</code> 和 <code>data</code> 能够拿到 <code>setup</code> 中的方法应该进行了合并，反之 <code>setup</code> 不能拿到它们的属性和方法，因为这个时候 <code>this = undefined</code></p>
<p><img src="http://test.imgurl.org/imgs/2023/01/31/867c38ac189566bf.png"></p>
<h4 id="Suspense-组件"><a href="#Suspense-组件" class="headerlink" title="Suspense 组件"></a><code>Suspense</code> 组件</h4><p><strong><code>setup</code> 使用 <code>async</code>&#x2F;<code>await</code></strong></p>
<p>我们需要 <code>setup</code> 返回数据那么它肯定就不能使用 <code>async</code> 修饰，这样返回 <code>promise</code> 是我们不想看见情况，如果我们硬要用 <code>async</code> 修饰，我们就得用的在它的父组件外层需要嵌套一个<code>suspense</code>(不确定)内置组件，里面放置一些不确定的操作，比如我们就可以把异步组件放入进去</p>
<p><strong>1.子组件</strong></p>
<pre><code>&lt;template&gt;
&#123;&#123; res &#125;&#125;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
import &#123; defineComponent &#125; from &#39;vue&#39;
export default defineComponent(&#123;
name: &#39;Son&#39;,
async setup() &#123;
    const res = await axios.get(&#39;地址&#39;)
    return &#123;
    res,
    &#125;
&#125;,
&#125;)
&lt;/script&gt;
</code></pre>
<p><strong>2.父组件</strong></p>
<pre><code>&lt;template&gt;
    &lt;Suspense&gt;
        &lt;!-- 子组件--&gt;
        &lt;Son&gt;&lt;/Son&gt;
    &lt;/Suspense&gt;
&lt;/template&gt;
</code></pre>
<h4 id="setup-参数"><a href="#setup-参数" class="headerlink" title="setup 参数"></a><code>setup</code> 参数</h4><p><code>setup</code>(<code>props</code>, <code>context</code>)</p>
<p><code>setup</code> 函数中的第一个参数是 <code>props</code>。它接收父组件传递的值，是的就是父子组件信息传递的 <code>props</code><br>第二个参数是 <code>context</code> 里面包含 3 个属性 <code>&#123; attrs, slots, emit &#125;</code>，这三个属性大家看名字就应该知道是什么吧 分别对应 <code>this.$attrs</code>，<code>this.$slots</code>，<code>this.$emit</code></p>
<ul>
<li><code>attrs</code>: 除了 <code>props</code> 中的其他属性</li>
<li><code>slots</code>: 父组件传入插槽内容的对象</li>
<li><code>emit</code>: 和用于父子组件通信</li>
</ul>
<h3 id="ref"><a href="#ref" class="headerlink" title="ref"></a><code>ref</code></h3><p><strong>定义&#x2F;转为 响应式</strong></p>
<p>在上面 <code>setup</code> 写的数据都<code>不是响应式的</code>，修改了数据，视图并不会更新</p>
<p>在 <code>Vue3</code> 中提供了两种方式定义响应式数据，先来介绍下 <code>ref</code></p>
<p>导入 <code>ref</code> 方法</p>
<pre><code>import &#123; defineComponent, ref &#125; from &#39;vue&#39;
</code></pre>
<ul>
<li><p>你可以先声明一个<strong>基本类型</strong>变量后再当做 <code>ref</code> 的形参穿进去</p>
</li>
<li><p>或者直接在 <code>ref</code> 中传入</p>
<p>setup() {<br>&#x2F;&#x2F; 方式一<br>let number1 &#x3D; ref(10)<br>let num &#x3D; 0<br>&#x2F;&#x2F; 方式二<br>let number2 &#x3D; ref(num)<br>return {}<br>},</p>
</li>
</ul>
<p>来查看一下 <code>number1</code> 是什么吧</p>
<p><img src="http://test.imgurl.org/imgs/2023/01/31/40c62feeda29af34.png"></p>
<p>可以看见的是 <code>number1</code> 是一个 <code>Ref</code> 对象，我们设置的 <code>10</code> 这个值在这个对象的 <code>value</code> 属性上<br>也就是说我们修改的时候必须要修改的是 <code>number1.value</code><br>通过给<code>value</code>属性添加 <code>getter</code>&#x2F;<code>setter</code> 来实现对数据的劫持<br>但是在模板上使用的时候 不用写 <code>number1.value</code> 直接写 <code>number1</code> 即可<br>在模板编译的时候回自动加上 <code>value</code></p>
<pre><code>&lt;template&gt;
&#123;&#123; number1 &#125;&#125;
&lt;button @click=&quot;updateNum&quot;&gt;+&lt;/button&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
import &#123; defineComponent, ref &#125; from &#39;vue&#39;
export default defineComponent(&#123;
name: &#39;Son&#39;,
setup() &#123;
    let number1 = ref(10)

    // 修改 number1
    function updateNum() &#123;
    number1.value++
    &#125;
    return &#123;
    number1,
    updateNum,
    &#125;
&#125;,
&#125;)
&lt;/script&gt;
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a5b9e88c52c84261aa28e6cc5cd74bf5~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>使用起来完全没有问题</p>
<p>刚才强调了说 <code>ref</code> 接收 基本类型的数据，那么它可以接收 <code>复杂类型吗，object</code> 类型等，当然可以</p>
<p>给 <code>ref</code> 传入复杂类型，其实它是调用 <code>reactive</code> 来实现的</p>
<p><code>reactive</code> 下面会提到</p>
<h3 id="ref-获取元素"><a href="#ref-获取元素" class="headerlink" title="ref 获取元素"></a>ref 获取元素</h3><p>同样的 <code>ref</code> 还可以用了获取元素</p>
<p>大家在<code> Vue2.X</code> 中是怎么获取的呢，先在 标签上定义 <code>:ref=&#39;XXX&#39;</code> 然后 <code>this.$refs.XXX </code>来获取</p>
<p>在 <code>Vue3</code> 上获取元素就有些许不同了</p>
<p><strong>1.首先在 模板元素上 <code>ref=&#39;XXX&#39;</code> 这里不用 <code>v-bind</code></strong></p>
<pre><code>&lt;template&gt;
&lt;div id=&quot;haha&quot; ref=&quot;haha&quot;&gt;&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p><strong>2.在 setup 中</strong></p>
<p>得给 <code>ref</code> 指定类型 <code>HTMLElement</code></p>
<pre><code>setup() &#123;
let haha = ref&lt;HTMLElement|null&gt;(null)
console.log(haha)

return &#123;
    haha,
&#125;
&#125;,
</code></pre>
<p>如果在组件中需要使用到 <code>haha</code> ，就必须把 <code>haha</code> <code>return</code> 出去合并 <code>data</code></p>
<p>我们来看看打印的是什么</p>
<p><img src="http://test.imgurl.org/imgs/2023/01/31/8ee2e3c4e4cc8115.png"></p>
<p>可以看见的是 <code>haha</code> 是个 <code>Ref</code> <code>对象，value</code> 值就是我们想要获取到的元素</p>
<p>然后我们可以对 <code>haha</code> 这个 <code>DOM</code> 元素进行操作，比如这个</p>
<p><strong>haha.style.fontSize &#x3D; ‘20px’</strong></p>
<h3 id="reactive"><a href="#reactive" class="headerlink" title="reactive"></a><code>reactive</code></h3><p><code>reactive</code> 接收一个普通对象然后返回该普通对象的响应式<code>代理对象</code></p>
<p>没错 它的底层就是使用 <code>Proxy</code> 进行代理</p>
<p>简单写个 Vue3 响应式例子来说下 <code>Proxy</code></p>
<p><code>new Proxy(target, handler)</code></p>
<ul>
<li><p><code>target</code> ：要使用 <code>Proxy</code> 包装的目标对象（可以是任何类型的对象，包括原生数组，函数，甚至另一个代理）</p>
</li>
<li><p><code>handler</code>：一个通常以函数作为属性的对象，各属性中的函数分别定义了在执行各种操作时代理 <code>p</code></p>
<p>&#x2F;&#x2F; 模拟 Vue data<br>let data &#x3D; {<br>msg: ‘’,<br>age: ‘’,<br>}<br>&#x2F;&#x2F; 模拟 Vue 的一个实例<br>&#x2F;&#x2F; Proxy 第一个<br>let vm &#x3D; new Proxy(data, {<br>&#x2F;&#x2F; get() 获取值<br>&#x2F;&#x2F; target 表示需要代理的对象这里指的就是 data<br>&#x2F;&#x2F; key 就是对象的 键<br>get(target, key) {<br>return target[key]<br>},<br>&#x2F;&#x2F; 设置值<br>&#x2F;&#x2F; newValue 是设置的值<br>set(target, key, newValue) {<br>&#x2F;&#x2F; 也先判断下是否和之前的值一样 节省性能<br>if (target[key] &#x3D;&#x3D;&#x3D; newValue) return<br>&#x2F;&#x2F; 进行设置值<br>target[key] &#x3D; newValue<br>document.querySelector(‘#app’).textContent &#x3D; target[key]<br>},<br>})</p>
<p><strong>reactive 基础用法</strong></p>
</li>
</ul>
<p>导入，当然写的时候，<code>vscode</code> 会自动帮你引入</p>
<pre><code>import &#123; defineComponent, reactive &#125; from &#39;vue&#39;
</code></pre>
<p>简单使用</p>
<pre><code>setup() &#123;
    let obj = reactive(&#123;
        name: &#39;小浪&#39;,
        age: 21,
    &#125;)
    return &#123;
        obj,
    &#125;
&#125;
</code></pre>
<p>来看看返回的 <code>Proxy</code> 对象吧</p>
<p><img src="http://test.imgurl.org/imgs/2023/01/31/7b1b28f3be53b165.png"></p>
<p>数据都在 <code>target</code> 中，</p>
<p>在模板使用直接 <code>&#123;&#123;obj.name&#125;&#125;</code> 即可</p>
<p>修改直接修改 <code>obj[name] = ‘xxx’</code></p>
<p>操作代理对象,obj 中的数据也会随之变化，同时如果想要在操作数据的时候,界面也要跟着重新更新渲染,那么也是操作代理对象</p>
<p><strong>响应式的数据是深层次的（递归深度响应式）</strong></p>
<p>对于多层嵌套的数据也是响应式的</p>
<pre><code>setup() &#123;
    let obj = reactive(&#123;
        name: &#39;小浪&#39;,
        age: 21,
        phone: &#123;
            p_name: &#39;小米&#39;,
            p_apps: &#123;
                app_name: &#39;小米运动&#39;,
            &#125;,
        &#125;,
    &#125;)
    function upadateName() &#123;
        obj.phone.p_apps.app_name = &#39;掘金&#39;
    &#125;
    console.log(obj)

    return &#123;
        obj,
        upadateName,
    &#125;
&#125;,
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa30d692e14e40cf962235006245e4c5~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p><strong>shallowReactive</strong></p>
<p>它是一个简单的 reactive ，只把第一层的对象改为响应式，这里就不多说了</p>
<p><strong>使用 ref 传入对象</strong></p>
<pre><code>setup() &#123;
    let obj = ref(&#123;
        name: &#39;小浪&#39;,
        age: 21,
    &#125;)
    console.log(obj)

    return &#123;
        obj,
    &#125;
&#125;
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/782b587ab1384163a505b3694b4eaab8~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>实际上是 <code>ref</code> 使用 <code>reactive</code> 来进行操作的</p>
<h3 id="toRefs"><a href="#toRefs" class="headerlink" title="toRefs"></a>toRefs</h3><p>这个方法可以把 <code>reactive</code> 响应式对象，转化为 普通对象，普通对象的每个属性都是 <code>Ref</code> 对象，这样的话保证了 <code>reactive</code> 的每个属性还是响应式的，我们还可以把每个属性进行分解使用，这样在组件就不用 <code>obj[属性]</code>，代码量减轻了，yyds</p>
<pre><code>setup() &#123;
const user = reactive(&#123;
    name: &#39;小浪&#39;,
    age: 21,
&#125;)

let userObj = toRefs(user)
console.log(userObj)

return &#123;&#125;
&#125;
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d44ff7b5950843c781ead5e0d1d64f2b~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>可以看见 <code>name</code> 和 <code>age</code> 已经变成了 <code>Ref</code> 对象</p>
<p>我们可以解构 出 <code>name</code> 和 <code>age</code> 单独使用</p>
<pre><code>setup() &#123;
const user = reactive(&#123;
    name: &#39;小浪&#39;,
    age: 21,
&#125;)

let userObj = toRefs(user)

return &#123;
    ...userObj,
&#125;
&#125;
</code></pre>
<h3 id="toRef"><a href="#toRef" class="headerlink" title="toRef"></a>toRef</h3><p>还有一个 <code>toRef</code> 方法，它的作用和 <code>toRefs</code> 差不多，但是它只能把响应式对象&#x2F;普通对象的<code>某一个</code>属性变为 <code>Ref</code> 对象</p>
<p><strong>可以用来为源响应式对象上的 <code>property</code> 性创建一个 <code>ref。然后可以将</code> <code>ref</code> 传递出去，从而保持对其源 <code>property</code> 的响应式连接。</strong></p>
<pre><code>export default &#123;
setup(props) &#123;
    useSomeFeature(toRef(props, &#39;foo&#39;))
&#125;
&#125;

function useSomeFeature(foo: Ref) &#123;
// ...
&#125;
</code></pre>
<p>拷贝了一份新的数据值单独操作, 更新时相互不影响<br>当您要将 <code>prop</code> 的 <code>ref</code> <code>传递给复合函数时，toRef</code> 很有用<br>可以从官方文档看出，用于在于组件之前的传递数据 从 <code>props</code> 拿出 <code>&#39;foo&#39;</code> 属性给复合函数，复合函数 <code>useSomeFeature，接收的参数</code> <code>foo</code> 为 <code>Ref</code> 类型，刚好可以使用<code>toRef</code> 来进行转化</p>
<h3 id="判断响应式"><a href="#判断响应式" class="headerlink" title="判断响应式"></a>判断响应式</h3><p><strong>几个判断是否哪种响应式创建的方法</strong></p>
<p>1.<code>isRef</code>: 检查一个值是否为一个 ref 对象</p>
<pre><code>let ref1 = ref(1)
console.log(isRef(ref1))  // true
</code></pre>
<p>2.<code>isReactive</code>: 检查一个对象是否是由 <code>reactive</code> 创建的响应式代理</p>
<pre><code>let ref2 = reactive(&#123;name: &#39;小浪&#39;&#125;)
console.log(isReactive(ref2))  // true
</code></pre>
<p>3.<code>isReadonly</code>: 检查一个对象是否是由 <code>readonly</code> 创建的只读代理</p>
<pre><code>let ref3 = readonly(&#123;name: &#39;小浪&#39;&#125;)
console.log(isReadonly(ref3))  // true
</code></pre>
<p>4.<code>isProxy</code>: 检查一个对象是否是由 <code>reactive</code> 或者 <code>readonly</code> 方法创建的代理</p>
<pre><code>let ref2 = reactive(&#123;name: &#39;小浪&#39;&#125;)
console.log(isProxy(ref2))  // true
let ref3 = readonly(&#123;name: &#39;小浪&#39;&#125;)
console.log(isProxy(ref3))  // true
</code></pre>
<h3 id="customRef"><a href="#customRef" class="headerlink" title="customRef"></a>customRef</h3><p>上面提到了这么多的 <code>Ref</code> 都是 Vue 帮我们内置的，</p>
<p>我们可以通过 <code>customRef</code> 实现我们自己的 <code>Ref</code></p>
<p>创建一个自定义的 ref，并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数，该函数接收 <code>track</code> 和 <code>trigger</code> 函数作为参数，并应返回一个带有 <code>get</code> 和 <code>set</code> 的对象。</p>
<p>官方文档给了一个防抖的例子，我们也写个来看</p>
<pre><code>&lt;template&gt;
&lt;h2&gt;App&lt;/h2&gt;
&lt;input v-model=&quot;keyword&quot;/&gt;
&lt;p&gt;&#123;&#123;keyword&#125;&#125;&lt;/p&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot;&gt;
import &#123;
customRef
&#125; from &#39;vue&#39;

// 不确定类型所以这里使用泛型
function useDebouncedRef&lt;T&gt;(value: T, delay = 200) &#123;
// 定时器
let timeout: number
return customRef((track, trigger) =&gt; &#123;
    return &#123;
    get() &#123;
        // 告诉Vue追踪数据
        track()
        return value
    &#125;,
    set(newValue: T) &#123;
        clearTimeout(timeout)
        timeout = setTimeout(() =&gt; &#123;
        value = newValue
        // 告诉Vue去触发界面更新
        trigger()
        &#125;, delay)
    &#125;
    &#125;
&#125;)
&#125;

export default &#123;
setup () &#123;
    const keyword = useDebouncedRef(&#39;&#39;)
    return &#123;
    keyword
    &#125;
&#125;,
&#125;
&lt;/script&gt;
</code></pre>
<h3 id="shallowRef-和-shallowReactive"><a href="#shallowRef-和-shallowReactive" class="headerlink" title="shallowRef 和 shallowReactive"></a><code>shallowRef</code> 和 <code>shallowReactive</code></h3><p>浅的响应式，一般用的不多，我们使用 <code>ref</code> 和 <code>reactive</code> 比较多</p>
<p><strong><code>shallowReactive</code></strong></p>
<p>对象结构多层嵌套，但是我们的需求只需要修改最外层的数据，就不用把里面的嵌套结构都转为响应式，这样使用浅的响应式提高性能，只有最外一层是响应式</p>
<p>比较容易理解，我这就里就不举例子了</p>
<p><strong><code>shallowRef</code></strong></p>
<p>我们之前说过 <code>ref</code> 也能传入一个对象，实际上还是调用 <code>reactive</code> 返回 <code>Proxy</code> 代理对象，如果内层还有对象，还是使用 <code>reactive</code> 进行处理</p>
<pre><code>ref(&#123; name: &#39;小明&#39; &#125;)
// 实际上是调用 reactive 去完成的，把 ref的 value 当作 key ,value的值，当作 key 的值 给 reactive
reactive(&#123; value: &#123; name: &#39;小明&#39; &#125; &#125;)
</code></pre>
<p>同样的 <code>shallowRef</code> 处理 对象类型，是交给 <code>shallowReactive</code> 去完成</p>
<pre><code>shallowRef(&#123; name: &#39;小明&#39; &#125;)
// 实际上是调用 reactive 去完成的，把 ref的 value 当作 key ,value的值，当作 key 的值 给 reactive
shallowReactive(&#123; value: &#123; name: &#39;小明&#39; &#125; &#125;)
</code></pre>
<p>这样子我们就明白了，为啥 只处理了 <code>value</code> 的响应式，不进行对象的 <code>reactive</code><br>处理，适用于会被替换的数据<br><code>【注意】：shallowRef</code> 创建一个 <code>ref</code> ，将会追踪它的 <code>value</code> 更改操作，但是并不会对变更后的 <code>value</code> 做响应式代理转换</p>
<pre><code>setup() &#123;
let info1 = ref(&#123;
    name: &#39;小浪&#39;,
    notebook: &#123;
    name: &#39;小米笔记本&#39;,
    &#125;,
&#125;)
let info2 = shallowRef(&#123;
    name: &#39;小明&#39;,
    notebook: &#123;
    name: &#39;小米笔记本&#39;,
    &#125;,
&#125;)

console.log(info1, info2)
return &#123;
    info1,
    info2,
&#125;
&#125;,
</code></pre>
<p>我们来打印下两个对象</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d3bce8ea859452dbea0388766f8308c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>可以看见的是 <code>Ref</code> 的 <code>value</code> 值是用 <code>reactive</code> 返回的 <code>Proxy</code> 对象，</p>
<p><code>shallowRef</code> 的 <code>value</code> 是普通对象</p>
<h3 id="readonly-和-shallowReadonly"><a href="#readonly-和-shallowReadonly" class="headerlink" title="readonly 和 shallowReadonly"></a><code>readonly</code> 和 <code>shallowReadonly</code></h3><p><strong><code>readonly</code> 深度只读</strong></p>
<p>设置普通对象或者是响应式对象为只读，不能进行修改，看上面的名字就知道是深度的，深度是什么概念大家差不多都清楚，递归把内层的每一个属性都设置为只读，进行修改操作就会报错，提高了安全性</p>
<p>基本使用：</p>
<p>用什么就导入什么</p>
<pre><code>import &#123; defineComponent, readonly &#125; from &#39;vue&#39;
</code></pre>
<p>果然在编译之前就报错了 <code>Error: 无法分配到 &quot;name&quot; ，因为它是只读属性</code></p>
<p>无论是内层还是外层都只读，是深度检测的</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/44875ed772764e8c9ccbf2eefc863c0e~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p><strong><code>shallowReadonly</code> 浅度只读</strong></p>
<p>浅度的话只针对最外面一层不关心 内层</p>
<p>可以看下面的例子 只有外层的 <code>name</code> 报错，修改内层没有错误</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d6262c6d96b44fcdb7012bd7f8da863e~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<h3 id="toRaw-和-markRaw"><a href="#toRaw-和-markRaw" class="headerlink" title="toRaw 和 markRaw"></a><code>toRaw</code> 和 <code>markRaw</code></h3><p><strong>这两个用的还是比较少</strong></p>
<p><strong>toRaw: 将一个响应式对象转为普通对象</strong></p>
<pre><code>setup() &#123;
let info1 = reactive(&#123;
    name: &#39;小浪&#39;,
    notebook: &#123;
    name: &#39;小米笔记本&#39;,
    &#125;,
&#125;)
const rawInfo = toRaw(info1) // 返回普通对象
console.log(info1)
console.log(rawInfo)

return &#123;&#125;
&#125;,
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0d99a9bfaa684c269495f99a48945ae7~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>两个打印出来，一个是响应式对象，通过 <code>toRaw</code> 后变成了普通对象</p>
<p><strong>markRaw: 标记一个对象，让它永远不会转为响应式对象，返回值是本身</strong></p>
<p>比如： 一些不变的数据死数据，还有一些第三方类实例，不用转为响应式对象，提高性能</p>
<p>简单使用：</p>
<p>这里使用 两个一样的对象，一个进行 <code>markRaw</code> 处理，一个不进行 <code>markRaw</code> 处理</p>
<p>然后同样使用 <code>reactive</code> 转为 响应式</p>
<pre><code>setup() &#123;
let obj = &#123;
    name: &#39;小浪&#39;,
    notebook: &#123;
    name: &#39;小米笔记本&#39;,
    &#125;,
&#125;
// 进行标记
let markRawObj = markRaw(obj)
// 尝试转为响应式
let reactObj = reactive(markRawObj)

let obj2 = &#123;
    name: &#39;小浪&#39;,
    notebook: &#123;
    name: &#39;小米笔记本&#39;,
    &#125;,
&#125;
// 转为响应式
let reactObj2 = reactive(obj2)

console.log(reactObj)
console.log(reactObj2)

return &#123;&#125;
&#125;
</code></pre>
<p>可以看看打印的，被标记过的 <code>obj</code> 并没有转为 <code>Proxy</code> 响应式代理对象</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04c6d80e6fb74a4d81b5e3167010ef79~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<h3 id="computed-计算属性"><a href="#computed-计算属性" class="headerlink" title="computed 计算属性"></a>computed 计算属性</h3><p>在 <code>Vue3</code> 中使用 <code>computed</code> 和 <code>Vue2.X</code> 有些不同，这里 <code>computed</code> 是一个方法</p>
<p><strong>首先还是得导入 <code>computed</code> 方法</strong></p>
<pre><code>import &#123; defineComponent, computed &#125; from &#39;vue&#39;
</code></pre>
<p><strong>参数为一个回调 默认为 <code>get</code></strong></p>
<pre><code>&lt;template&gt;
&lt;div class=&quot;box&quot;&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;name&quot; /&gt;
    &lt;br /&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;age&quot; /&gt;
    &lt;br /&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;getInfo&quot; /&gt;
&lt;/div&gt;
&lt;/template&gt;

setup() &#123;
    let name = ref(&#39;小浪&#39;)
    let age = ref(21)

    //计算属性
    let getInfo = computed(() =&gt; &#123;
        return `我的名字：$&#123;name.value&#125;,今年$&#123;age.value&#125;，请多多指教`
    &#125;)
    return &#123;
        name,
        age,
        getInfo,
    &#125;
&#125;
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/336f33d348ed4e908e016a8c9eacb796~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>这里没有实现 <code>set</code> 方法，所以修改下面没有用</p>
<p>参数为一个<code>对象</code> 在这里写 <code>get</code> <code>set</code></p>
<p>模板和上面一样</p>
<pre><code>setup() &#123;
    let name = ref(&#39;小浪&#39;)
    let age = ref(21)

    let getInfo = computed(&#123;
        // get 方法
        get() &#123;
            return `$&#123;name.value&#125;,$&#123;age.value&#125;`
        &#125;,
        // set 方法
        set(val: string) &#123;
            let arr = val.split(&#39;,&#39;)
            name.value = arr[0]
            age.value = parseInt(arr[1])
        &#125;,
    &#125;)
    return &#123;
        name,
        age,
        getInfo,
    &#125;
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb1487dddb3f425e9d9aa2460d202b5c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<h3 id="watch-侦听器"><a href="#watch-侦听器" class="headerlink" title="watch 侦听器"></a>watch 侦听器</h3><p>和 Vue2.X 的 Watch 使用方法差不多</p>
<p><strong>介绍</strong></p>
<p>watch(data,handler,object)</p>
<p>1.<code>data</code>：可以是返回值的 <code>getter</code> 函数，也可以是 <code>ref</code></p>
<p>2.<code>handler</code>：回调函数</p>
<p>3.<code>object</code>：可选配置项 <code>&#123; immediate: true &#125;</code></p>
<p><strong>引入</strong></p>
<pre><code>import &#123; defineComponent, watch &#125; from &#39;vue&#39;
</code></pre>
<p><strong><code>data</code> 为一个 <code>ref</code></strong></p>
<p>回调函数的参数是 (新值,旧值)</p>
<pre><code>setup() &#123;
    let name = ref(&#39;小浪&#39;)
    let age = ref(21)

    let watchName = ref(&#39;&#39;)
    watch(name, (newName, oldName) =&gt; &#123;
        watchName.value = `我是新姓名$&#123;newName&#125;
        我是老姓名$&#123;oldName&#125;`
    &#125;)
    return &#123;
        name,
        age,
        watchName,
    &#125;
&#125;,
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a7d69bf6172f4f68be61571853880c41~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p>可以看见页面第三栏没有显示，因为 <code>name</code> 值没有变化，所以就不用改变，<code>watch</code>的第三个参数是 配置对象，我们在里面可以设置 立即执行 <code>&#123; immediate: true &#125;</code></p>
<p>就会执行一次 当然这个时候 <code>oldName</code> 为 <code>undefined</code></p>
<pre><code>watch(
name,
(newName, oldName) =&gt; &#123;
    watchName.value = `我是新姓名$&#123;newName&#125;
我是老姓名$&#123;oldName&#125;`
&#125;,
&#123; immediate: true &#125;
)
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3e35c8ce9360449e9edaa4bbaa00336a~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p><strong><code>data</code> 为一个 <code>getter</code></strong></p>
<pre><code>watch(()=&gt;haha,(newName, oldName)=&gt;&#123;
// 处理...
&#125;)
</code></pre>
<p><code>()=&gt; haha</code> 直接返回一个值，相当于 <code>getter</code> 简写,<code>haha</code>可以不是响应式数据</p>
<p><code>data</code> 为多个 <code>ref</code></p>
<p>模板还是之前那个</p>
<pre><code>&lt;template&gt;
&lt;div class=&quot;box&quot;&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;name&quot; /&gt;
    &lt;br /&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;age&quot; /&gt;
    &lt;br /&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;getInfo&quot; /&gt;
&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p>我们可以把多个 <code>ref</code> 放进一个数组里面</p>
<p><code>newNameAndAge</code>，<code>oldNameAndAge</code>为一个数组保存着 新 和 旧的 [name,age]</p>
<pre><code>setup() &#123;
    let name = ref(&#39;小浪&#39;)
    let age = ref(21)

    let watchName = ref(&#39;&#39;)
    watch(
    [name, age],
    (newNameAndAge, oldNameAndAge) =&gt; &#123;
        watchName.value = `new: $&#123;newNameAndAge&#125;
        old: $&#123;oldNameAndAge&#125;`
    &#125;,
    &#123; immediate: true &#125;
    )
    return &#123;
        name,
        age,
        watchName,
    &#125;
&#125;,
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/60adfa12c0dc42239985d630a0a9e46d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<p><strong><code>data</code> 为 <code>reactive</code></strong></p>
<pre><code>setup() &#123;
let user = reactive(&#123;
    name: &#39;小浪&#39;,
    age: 21,
&#125;)

let watchInfo = ref(&#39;&#39;)
watch(
    user,
    (newInfo, oldInfo) =&gt; &#123;
    console.log(newInfo === oldInfo)  // true
    &#125;
)
&#125;
</code></pre>
<p>这里是对象 会出现问题，立即执行后，<br>如果加上 立即执行 除了第一次 <code>newInfo</code>为 <code>&#123;name: &#39;小浪&#39;,age: 21&#125;</code><br><code>oldInfo</code> 为 <code>undefined</code> ，之后<code>始终返回该对象的当前值</code><br>所以 <code>newInfo</code> &#x3D; <code>oldInfo</code><br>对于这个问题，我们得加上配置对象 <code>&#123;deep: true&#125;</code>进行深度检测<br>深度检测还可以判断多重嵌套</p>
<pre><code>watch(
user,
(newInfo, oldInfo) =&gt; &#123;
    console.log(newInfo === oldInfo) // false
&#125;,
&#123; deep: true &#125;
)
</code></pre>
<h3 id="watchEffect"><a href="#watchEffect" class="headerlink" title="watchEffect"></a>watchEffect</h3><p>这个也是用来监听数据变化，默认就会执行一次所以这里就不需要配置，而且不用指定 <code>data</code>,使用哪些响应式数据就监听哪些</p>
<pre><code>let user = reactive(&#123;
    name: &#39;小浪&#39;,
    age: 21,
&#125;)
// 只有 user.name 发生改变这个就会执行
watchEffect(() =&gt; &#123;
    console.log(user.name)
&#125;);
</code></pre>
<h3 id="provide-x2F-inject"><a href="#provide-x2F-inject" class="headerlink" title="provide &#x2F; inject"></a>provide &#x2F; inject</h3><p>提供 和 注入 是很简单理解的</p>
<p><strong>实现跨层级组件(祖孙)间通信</strong></p>
<p>在多层嵌套组件中使用，不需要将数据一层一层地向下传递</p>
<p>可以实现 跨层级组件 通信</p>
<p>在 父组件中</p>
<pre><code>setup() &#123;
    const info = reactive(&#123;
        title: &#39;Vue3学习&#39;
        date: &#39;2021/7/23&#39;
    &#125;)
    // 提供数据 提供的数据名，数据值
    provide(&#39;info&#39;, info)

    return &#123;
        info
    &#125;
&#125;
</code></pre>
<p>在 子孙 层级组件使用注入就能够获取到了</p>
<pre><code>setup() &#123;
    //获取对应数据的值
    const color = inject(&#39;info&#39;)

    return &#123;
        info
    &#125;
&#125;
</code></pre>
<h3 id="Teleport-传送组件"><a href="#Teleport-传送组件" class="headerlink" title="Teleport 传送组件"></a><code>Teleport</code> 传送组件</h3><p>这个组件特别有趣，可以把组件进行传送</p>
<pre><code>&lt;teleport v-if=&quot;flag&quot; to=&quot;.test&quot;&gt;
    &lt;div class=&quot;dog&quot;&gt;狗子&lt;/div&gt;
&lt;/teleport&gt;
</code></pre>
<p><code>to</code> 是目标的地址 <code>body</code> , <code>#XXX </code>, <code>.XXX</code> 这些都是 <code>css</code> 选择器</p>
<p><strong>模板</strong></p>
<pre><code>&lt;template&gt;
&lt;ul&gt;
    &lt;li class=&quot;li_1&quot;&gt;&lt;/li&gt;
    &lt;li class=&quot;li_2&quot;&gt;&lt;/li&gt;
    &lt;li class=&quot;li_3&quot;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;teleport :to=&quot;target&quot;&gt;
    &lt;img src=&quot;https://img0.baidu.com/it/u=3077713857,1222307962&amp;fm=26&amp;fmt=auto&amp;gp=0.jpg&quot; /&gt;
&lt;/teleport&gt;
&lt;div class=&quot;btnGroup&quot;&gt;
    &lt;button @click=&quot;target = &#39;.li_1&#39;&quot;&gt;传送1&lt;/button&gt;
    &lt;button @click=&quot;target = &#39;.li_2&#39;&quot;&gt;传送2&lt;/button&gt;
    &lt;button @click=&quot;target = &#39;.li_3&#39;&quot;&gt;传送3&lt;/button&gt;
&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p><strong>setup</strong></p>
<pre><code>setup() &#123;
// target
let target = ref(&#39;.li_1&#39;)
return &#123;
    target,
&#125;
&#125;,
</code></pre>
<p>利用 按钮 点击来控制 <code>teleport</code> 是否显示， <code>teleport</code> 一渲染，就会跑到 <code>li</code> 下面</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/083b7f7bcd8b46179188e4a5cfc31454~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp"></p>
<h3 id="Vue3-生命周期"><a href="#Vue3-生命周期" class="headerlink" title="Vue3 生命周期"></a>Vue3 生命周期</h3><p><code>Vue2.X</code> 对应 <code>Vue3</code>组合 API</p>
<p><img src="http://test.imgurl.org/imgs/2023/02/01/ee9d03d7ddf9e0cf.png"></p>
<p>可以看出<br><code>beforeCreate</code> 和 <code>created</code> 在 Vu3 还是能正常使用，在 Vue3 我们可以用更好更快的 <code>setup</code> 代替<br><code>on</code>开头的 生命周期需要 通过 <code>import</code> 导入，在 <code>setup</code> 函数中使用<br><code>Vue3</code> 的生命周期 比 <code>Vue2.X</code> 的生命周期快<br>举个例子: <code>onBeforeMount</code> 比 <code>beforeMount</code> 快 其他同理<br>还多个两个钩子:</p>
<ul>
<li>onRenderTriggered 跟踪虚拟 DOM 重新渲染时调用</li>
<li>onRenderTracked 当虚拟 DOM 重新渲染被触发时调用</li>
</ul>
<h3 id="全局-API-转移"><a href="#全局-API-转移" class="headerlink" title="全局 API 转移"></a>全局 API 转移</h3><p><code>Vue2.X</code> 中 <code>Vue</code> 上面的全局 API ，比如自定义指令 <code>Vue.directive</code>，全局组件 <code>Vue.component</code> 在 <code>Vue3</code> 都进行改变，不再提供 <code>Vue</code> ，而是提供 <code>app</code></p>
<p><img src="http://test.imgurl.org/imgs/2023/02/01/5b9fdd16aa6c08e4.png"></p>
<h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>到了这里我们基本都了解了 <code>Vue3</code> 的一些特性</p>
<ul>
<li><p>新的脚手架工具<code>vite</code></p>
</li>
<li><p>在 <code>Vue3</code> 仍然支持 <code>Vue2</code> 中的大多数特性</p>
</li>
<li><p><code>Vue</code> 组合<code>APi</code>代替了<code>Vue2</code>中的<code>option API</code> ，同一逻辑集中起来，复用性更强了</p>
</li>
<li><p><code>Vue3</code> 使用 <code>TS</code> 编写更好的支持 TS</p>
</li>
<li><p><code>Vue3</code> 使用<code>Proxy</code> 代替了<code>Vue2</code>中<code>Object.defineProperty()</code> 实现响应式原理</p>
</li>
<li><p>介绍了新的组件: <code>Fragment</code> <code>Teleport</code> <code>Suspense</code></p>
</li>
</ul>
<p>这里还没有提到的 <code>Vue3</code> 重写了 <code>虚拟DOM</code> ，提高了性能</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="https://gitee.com/dmf-c/blog">dmf</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://gitee.com/dmf-c/blog/2023/01/08/vue_file/vuenote/">https://gitee.com/dmf-c/blog/2023/01/08/vue_file/vuenote/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://gitee.com/dmf-c/blog" target="_blank">dmf的个人博客</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"></div><div class="post_share"><div class="social-share" data-image="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="next-post pull-full"><a href="/blog/2022/02/20/vue_file/comm/"><img class="next-cover" src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="onerror=null;src='/blog/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">vue组件间通信六种方式</div></div></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="this.onerror=null;this.src='/blog/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">dmf</div><div class="author-info__description"></div></div><div class="card-info-data site-data is-center"><a href="/blog/archives/"><div class="headline">文章</div><div class="length-num">27</div></a><a href="/blog/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/blog/categories/"><div class="headline">分类</div><div class="length-num">6</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">业精于勤，荒于嬉。</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Vue3-%E7%AE%80%E4%BB%8B"><span class="toc-number">1.</span> <span class="toc-text">Vue3 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9F%BA%E7%A1%80%E5%B7%A5%E4%BD%9C"><span class="toc-number">2.</span> <span class="toc-text">基础工作</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85-vue-cli"><span class="toc-number">2.1.</span> <span class="toc-text">安装 vue-cli</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E9%A1%B9%E7%9B%AE"><span class="toc-number">2.2.</span> <span class="toc-text">创建一个项目</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Vue3-%E5%85%A5%E9%97%A8"><span class="toc-number">3.</span> <span class="toc-text">Vue3 入门</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Composition-API"><span class="toc-number">3.1.</span> <span class="toc-text">Composition API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Fragment"><span class="toc-number">3.2.</span> <span class="toc-text">Fragment</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#script-%E5%B7%AE%E5%BC%82"><span class="toc-number">3.3.</span> <span class="toc-text">script 差异</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#setup"><span class="toc-number">3.4.</span> <span class="toc-text">setup</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#setup-%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F"><span class="toc-number">3.4.1.</span> <span class="toc-text">setup 执行顺序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#setup-%E8%BF%94%E5%9B%9E%E5%80%BC"><span class="toc-number">3.4.2.</span> <span class="toc-text">setup 返回值</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Suspense-%E7%BB%84%E4%BB%B6"><span class="toc-number">3.4.3.</span> <span class="toc-text">Suspense 组件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#setup-%E5%8F%82%E6%95%B0"><span class="toc-number">3.4.4.</span> <span class="toc-text">setup 参数</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ref"><span class="toc-number">3.5.</span> <span class="toc-text">ref</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ref-%E8%8E%B7%E5%8F%96%E5%85%83%E7%B4%A0"><span class="toc-number">3.6.</span> <span class="toc-text">ref 获取元素</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#reactive"><span class="toc-number">3.7.</span> <span class="toc-text">reactive</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#toRefs"><span class="toc-number">3.8.</span> <span class="toc-text">toRefs</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#toRef"><span class="toc-number">3.9.</span> <span class="toc-text">toRef</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%A4%E6%96%AD%E5%93%8D%E5%BA%94%E5%BC%8F"><span class="toc-number">3.10.</span> <span class="toc-text">判断响应式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#customRef"><span class="toc-number">3.11.</span> <span class="toc-text">customRef</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#shallowRef-%E5%92%8C-shallowReactive"><span class="toc-number">3.12.</span> <span class="toc-text">shallowRef 和 shallowReactive</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#readonly-%E5%92%8C-shallowReadonly"><span class="toc-number">3.13.</span> <span class="toc-text">readonly 和 shallowReadonly</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#toRaw-%E5%92%8C-markRaw"><span class="toc-number">3.14.</span> <span class="toc-text">toRaw 和 markRaw</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#computed-%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7"><span class="toc-number">3.15.</span> <span class="toc-text">computed 计算属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#watch-%E4%BE%A6%E5%90%AC%E5%99%A8"><span class="toc-number">3.16.</span> <span class="toc-text">watch 侦听器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#watchEffect"><span class="toc-number">3.17.</span> <span class="toc-text">watchEffect</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#provide-x2F-inject"><span class="toc-number">3.18.</span> <span class="toc-text">provide &#x2F; inject</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Teleport-%E4%BC%A0%E9%80%81%E7%BB%84%E4%BB%B6"><span class="toc-number">3.19.</span> <span class="toc-text">Teleport 传送组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue3-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-number">3.20.</span> <span class="toc-text">Vue3 生命周期</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%A8%E5%B1%80-API-%E8%BD%AC%E7%A7%BB"><span class="toc-number">3.21.</span> <span class="toc-text">全局 API 转移</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%93%E8%AF%AD"><span class="toc-number">4.</span> <span class="toc-text">结语</span></a></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/blog/2023/01/08/vue_file/vuenote/" title="vue3入门笔记"><img src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="vue3入门笔记"/></a><div class="content"><a class="title" href="/blog/2023/01/08/vue_file/vuenote/" title="vue3入门笔记">vue3入门笔记</a><time datetime="2023-01-07T16:00:00.000Z" title="发表于 2023-01-08 00:00:00">2023-01-08</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2022/02/20/vue_file/comm/" title="vue组件间通信六种方式"><img src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="vue组件间通信六种方式"/></a><div class="content"><a class="title" href="/blog/2022/02/20/vue_file/comm/" title="vue组件间通信六种方式">vue组件间通信六种方式</a><time datetime="2022-02-19T16:00:00.000Z" title="发表于 2022-02-20 00:00:00">2022-02-20</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2022/02/05/wxEcharts/" title="微信小程序使用 echarts"><img src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="微信小程序使用 echarts"/></a><div class="content"><a class="title" href="/blog/2022/02/05/wxEcharts/" title="微信小程序使用 echarts">微信小程序使用 echarts</a><time datetime="2022-02-04T16:00:00.000Z" title="发表于 2022-02-05 00:00:00">2022-02-05</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2022/01/30/vue_file/vue_router/" title="vue-router学习"><img src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="vue-router学习"/></a><div class="content"><a class="title" href="/blog/2022/01/30/vue_file/vue_router/" title="vue-router学习">vue-router学习</a><time datetime="2022-01-29T16:00:00.000Z" title="发表于 2022-01-30 00:00:00">2022-01-30</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2022/01/16/vue_file/vuex/" title="Vuex使用方法"><img src="https://img1.baidu.com/it/u=1828439677,2166285235&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=400&amp;h=300" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="Vuex使用方法"/></a><div class="content"><a class="title" href="/blog/2022/01/16/vue_file/vuex/" title="Vuex使用方法">Vuex使用方法</a><time datetime="2022-01-15T16:00:00.000Z" title="发表于 2022-01-16 00:00:00">2022-01-16</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By dmf</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/blog/js/utils.js"></script><script src="/blog/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>